/**
 * @file BM77SSP03.c
 * @brief bluetooth Interface
 *
 * @version Fixed
 * @author lbx
 * @date 2014-05-04
 */
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include "console.h"
#include "bt_gpio.h"
#include "device.h"

static bt_retfunc_t bt_retfunc;
static char bt_retbuf[64];
static int bt_fd = -1;

static void bt_get_laddr(int argc, const char **argv);
static void bt_get_name(int argc, const char **argv);
static void bt_get_status(int argc, const char **argv);
static void bt_set_wakeup(int argc, const char **argv);
static void bt_set_adv(int argc, const char **argv);
static void bt_set_disconnect(int argc, const char **argv);
static void bt_set_mode(int argc, const char **argv);

const command_t BT_CMD[] = {
	{"+LADDR", bt_get_laddr, NULL},
	{"+NAME", bt_get_name, NULL},
	{"+STATUS", bt_get_status,NULL},
	{"+WAKE", bt_set_wakeup, NULL},
	{"+ADV", bt_set_adv, NULL},
	{"+DISC", bt_set_disconnect, NULL},
	{"#CSMODE", bt_set_mode, NULL},
};

void bt_dev_init(void)
{
    bt_fd = open(CONFIG_BT_DEV, O_RDWR|O_NONBLOCK);
	bt_retfunc = NULL;
	memset(bt_retbuf, 0, sizeof(bt_retbuf));
}

static int bt_retprintf(uint8_t * buffer, size_t len)
{
	len = len > strlen(bt_retbuf) ? strlen(bt_retbuf) + 1 : len;
	memcpy(buffer, bt_retbuf, len);

	return len;
}

int bt_is_enable(void)
{
	bt_status_t state;
	
	state = bt_status();
	if(state < BT_STAUS_SHUTDOWN || state > BT_STAUS_LINK)
		return 0;	
	else 
		return state - BT_STAUS_SHUTDOWN;
}

static void bt_get_laddr(int argc, const char **argv)
{
	if (argc != 1) {
		strcpy(bt_retbuf, "ERROR\r\n");
		bt_retfunc = bt_retprintf;
	}
	else {
		strcpy(bt_retbuf, "+LADDR=00:11:67:50:00:00\r\nOK\r\n");
		bt_retfunc = bt_retprintf;
	}
}

static void bt_get_name(int argc, const char **argv)
{
	if (argc != 1) {
		strcpy(bt_retbuf, "ERROR\r\n");
		bt_retfunc = bt_retprintf;
	}
	else {
		strcpy(bt_retbuf, "+NAME=\"Dual-SPP\"\r\nOK\r\n");
		bt_retfunc = bt_retprintf;
	}
}

static void bt_get_status(int argc, const char **argv)
{
	bt_status_t state;
	
	if (argc != 1) {
		strcpy(bt_retbuf, "ERROR\r\n");
		bt_retfunc = bt_retprintf;
	}
	else {
		state = bt_status();
		if(state < BT_STAUS_SHUTDOWN || state > BT_STAUS_LINK)
			strcpy(bt_retbuf, "ERROR\r\n");
		else {
			sprintf(bt_retbuf, "+STATUS=%d\r\nOK\r\n", state - BT_STAUS_SHUTDOWN);
		}
		bt_retfunc = bt_retprintf;
	}
}

static void bt_set_wakeup(int argc, const char **argv)
{
	if (argc != 1) {
		strcpy(bt_retbuf, "ERROR\r\n");
		bt_retfunc = bt_retprintf;
	}
	else {
		bt_wake_up();
		strcpy(bt_retbuf, "OK\r\n");
		bt_retfunc = bt_retprintf;
	}
}

static void bt_set_adv(int argc, const char **argv)
{
	if (argc != 1) {
		strcpy(bt_retbuf, "ERROR\r\n");
		bt_retfunc = bt_retprintf;
	}
	else {
		bt_advertising();
		strcpy(bt_retbuf, "OK\r\n");
		bt_retfunc = bt_retprintf;
	}
}

static void bt_set_disconnect(int argc, const char **argv)
{
	if (argc != 1) {
		strcpy(bt_retbuf, "ERROR\r\n");
		bt_retfunc = bt_retprintf;
	}
	else {
		bt_disconnect();
		strcpy(bt_retbuf, "OK\r\n");
		bt_retfunc = bt_retprintf;
	}
}

static void bt_set_mode(int argc, const char **argv)
{
	if (argc != 2) {
		strcpy(bt_retbuf, "ERROR\r\n");
		bt_retfunc = bt_retprintf;
	}
	else {
		uint32_t mode = strtol(argv[1], NULL, 10);
		bt_mode(mode);
		strcpy(bt_retbuf, "OK\r\n");
		bt_retfunc = bt_retprintf;
	}
}

int bt_cmd_return(uint8_t * buffer, size_t len)	  //  called by: serial_read()
{
	int ret;
	if (bt_retfunc != NULL) {
		ret = bt_retfunc(buffer, len);
		bt_retfunc = NULL;
		return ret;
	}
	else
		return 0;
}

enum ParseState {
        PS_WHITESPACE,
        PS_TOKEN,
        PS_STRING,
        PS_ESCAPE
};
static enum ParseState stackedState;
static void parseargs(char *argstr, int *argc_p, char **argv, char** resid)
{
        int argc = 0;
        char c;
        enum ParseState lastState = PS_WHITESPACE;

        /* tokenize the argstr */
        while ((c = *argstr) != 0) {
                enum ParseState newState;

                if (c == ';' && lastState != PS_STRING && lastState != PS_ESCAPE)
                        break;

                if (lastState == PS_ESCAPE) {
                        newState = stackedState;
                } else if (lastState == PS_STRING) {
                        if (c == '"') {
                                newState = PS_WHITESPACE;
                                *argstr = 0;
                        } else {
                                newState = PS_STRING;
                        }
                } else if ((c == ' ') || (c == '\t')) {
                        /* whitespace character */
                        *argstr = 0;
                        newState = PS_WHITESPACE;
                } else if (c == '"') {
                        newState = PS_STRING;
                        *argstr++ = 0;
                        argv[argc++] = argstr;
                } else if (c == '\\') {
                        stackedState = lastState;
                        newState = PS_ESCAPE;
                } else {
                        /* token */
                        if (lastState == PS_WHITESPACE) {
                                argv[argc++] = argstr;
                        }
                        newState = PS_TOKEN;
                }

                lastState = newState;
                argstr++;
        }

        argv[argc] = NULL;
        if (argc_p != NULL)
                *argc_p = argc;

        if (*argstr == ';') {
                *argstr++ = '\0';
        }
        *resid = argstr;
}

static command_t *find_cmd(const char *cmdname)
{
        command_t *cmd;
		int cmd_conut = sizeof(BT_CMD) / sizeof(BT_CMD[0]);

        if (cmdname == NULL) return NULL;

		
        for (cmd = BT_CMD;
                        cmd < cmd_conut; cmd ++) {

                if (strcmp(*cmd->name, cmdname) == 0) {
                        return cmd;
                }
        }
        return NULL;
}

static int exec_cmd(int argc, const char **argv)
{
        command_t *cmd = find_cmd(argv[0]);

        if (cmd == NULL) {
                return 0;
        }
        cmd->cmdfunc(argc, argv);
		return 1;
}

int handle_bt_at(char *argstr)
{
	int argc;
	char *argv[128];
	char *resid;

	memset(argv, 0, sizeof(argv));
	parseargs(argstr, &argc, argv, &resid);
	if (argc > 0) {
   		return exec_cmd(argc, (const char **)argv);
	}

	return 0;
}


